Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 <<   zurück
Visual Basic 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual Basic 2005

Visual Basic 2005
1.233 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-585-1
gp Kapitel 24 Programmiertechniken
  gp 24.1 Drag
    gp 24.1.1 Der Ablauf einer Drag
    gp 24.1.2 Das Einleiten einer Drag
    gp 24.1.3 Die Ereignisse des Empfängers einer Drag
    gp 24.1.4 Programmbeispiele
  gp 24.2 Entwickeln vom Windows-Dienstanwendungen
    gp 24.2.1 Einführung
    gp 24.2.2 »Windows-Dienst«-Projekte in der Entwicklungsumgebung
    gp 24.2.3 Die Methoden eines Dienstes
    gp 24.2.4 Die Eigenschaften eines Dienstes
    gp 24.2.5 Die Installation eines Windows-Dienstes
    gp 24.2.6 Beispielprogramm »FileWatchService«


Galileo Computing

24.2 Entwickeln vom Windows-Dienstanwendungen  downtop


Galileo Computing

24.2.1 Einführung  downtop

Windows-Dienste sind Anwendungen, die automatisch geladen werden, wenn der Computer gebootet wird. Ein besonderes Merkmal dieser Anwendungen ist, dass sie keine Benutzer-oberfläche aufweisen. Sie operieren im Hintergrund, werden vom Anwender nicht wahrgenommen und führen dennoch kontinuierlich Aufgaben aus, zum Beispiel die zur Überwachung.

Die Verwaltung aller Dienste wird von einer Komponente namens Service Control Manager (SCM) übernommen. Die Dienste eines Rechners werden in der Microsoft Management Console (MMC) angezeigt, die Sie sich über Systemsteuerung N Verwaltung N Dienste anzeigen lassen können. Eine weitere Möglichkeit bietet das Visual Studio 2005 mit dem Server-Explorer, der standardmäßig am linken Rand der IDE angeordnet ist und nicht nur bei der Entwicklung von Windows-Diensten eine Erleichterung darstellt, weil nicht immer zwischen verschiedenen Fenstern gewechselt werden muss.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 24.1     Der »Server-Explorer« von Visual Studio 2005

Ein installierter Dienst trägt sich in die Registrierungsdatenbank ein. Wird das System hochgefahren, werden die installierten Dienste geladen und stehen danach dem Service Control Manager zur Verfügung. Sie müssen sich im Zusammenhang mit den Windows-Diensten ein typisches Merkmal dieser Anwendungen merken: Einen Dienst zu laden bedeutet nicht, dass er gleichzeitig auch gestartet wird und seine ihm zugedachte Aufgabe ausführt.

Das Startverhalten ist eine Eigenschaft, die jedem Dienst eigen ist und drei Konfigurationswerte annehmen kann:

gp  Manuell
gp  Automatisch
gp  Deaktiviert

Ein automatisch gestarteter Dienst ist von Anfang an aktiv. Ein manuell zu startender Dienst kann entweder über seine Eigenschaften im Dienste-Dialog oder programmiertechnisch aus einer anderen Anwendung heraus gestartet werden. Deaktivierte Dienste lassen sich weder vom Benutzer noch von einem anderen Programm starten. Ein gestarteter Dienst kann während seiner Ausführungszeit angehalten oder beendet werden.

Ein Dienst, der beendet wird, wird gleichzeitig auch entladen. Das ist der Unterschied zu einem Dienst, dessen Laufzeit nur angehalten worden ist, dessen Code sich aber weiterhin im Speicher befindet. Sowohl beendete als auch angehaltene Dienste können erneut gestartet werden. Allerdings muss ein beendeter Dienst dazu neu geladen werden.

In der Abbildung 24.2 ist der Eigenschaftsdialog des Nachrichtendienstes zu sehen, dessen standardmäßig automatisches Starten auf manuelles Starten umgestellt worden ist. Um dieses Dialogfenster zu öffnen, markieren Sie im Dienste-Dialog den Dienst, öffnen das Kontextmenü und wählen Eigenschaften. Der Eigenschaftsdialog dient auch administrativen Aufgaben, denn Sie können hier nicht nur das Startverhalten des Dienstes umstellen, sondern einen Dienst auch starten, beenden, anhalten oder fortsetzen. Dienste, die in der Lage sind, Startparameter entgegenzunehmen, zeigen ein aktiviertes Eingabefeld am unteren Rand der Registerkarte Allgemein. Ausschließlich informativen Charakter hat die Angabe der Datei, die den Dienst bereitstellt. In der Abbildung 24.2 ist es die Datei services.exe.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 24.2     Das Eigenschaftsfenster des Nachrichtendienstes


Galileo Computing

24.2.2 »Windows-Dienst«-Projekte in der Entwicklungsumgebung  downtop

Nach dieser Einführung in die Welt der Windows-Dienste wollen wir natürlich auch sehen, wie wir einen Dienst mit dem Visual Studio .NET programmieren. Dazu müssen Sie zunächst eine andere Projektvorlage wählen: Windows-Dienst. Als Projektbezeichner wird der Name FileWatchService vergeben.

Wir sollten zunächst einen Blick auf das Grundgerüst des automatisch erzeugten Programmcodes werfen, der sich von dem einer Konsolen- oder Windows-Anwendung in wesentlichen Punkten unterscheidet.


' Dies ist der Inhalt der Datei Service1.vb
Public Class Service1
Protected Overrides Sub OnStart(ByVal args() As String)
End Sub
Protected Overrides Sub OnStop()
End Sub
End Class
' Dies ist der Inhalt der Datei Service1.Designer.vb,
' reduziert auf den für uns interessanten Code
Imports System.ServiceProcess
Partial Class Service1
Inherits System.ServiceProcess.ServiceBase
Shared Sub Main()
Dim ServicesToRun() As ServiceBase
ServicesToRun = New ServiceBase() {New Service1}
ServiceBase.Run(ServicesToRun)
End Sub
End Class

Die Vorgabe legt den Klassennamen auf Service1 fest. Die Klasse ist von ServiceBase abgeleitet, welche die Basisklasse der Windows-Dienste ist und zum Namespace System.ServiceProcess gehört. Wie jedes andere Programm wird auch ein Dienst durch den Aufruf der Methode Main gestartet. Was dabei passiert, ist bereits festgelegt. Zuerst wird ein Array vom Typ ServiceBase deklariert und diesem die Referenz auf das Dienst-Objekt vom Typ Service1 übergeben. Damit ist es auch möglich, innerhalb einer Windows-Dienstanwendung mehrere Dienste gleichzeitig bereitzustellen. Jeder Dienst ist dabei als eine Klasse definiert, die von ServiceBase abgeleitet ist. Die Initialisierung des Arrays muss dann nur durch die entsprechenden Objektreferenzen ergänzt werden. Im letzten Schritt wird die statische Methode Run der Klasse ServiceBase aufgerufen, und alle Dienste werden geladen. Ist die Methode ausgeführt, übernimmt der Service Control Manager die Steuerung der Dienste.

Zwei geschützte Methoden der Basisklasse werden bereits überschrieben bereitgestellt: OnStart und OnStop. Hierbei handelt es sich um die Methoden, die ausgeführt werden, wenn der Dienst gestartet beziehungsweise beendet wird. OnStart enthält damit logischerweise die Funktionalität des Dienstes, in OnStop implementieren Sie den Code, der ausgeführt werden soll, wenn der Dienst beendet wird.

Bei OnStart und OnStop handelt es sich genau genommen um Ereignisse, die aber von der Basisklasse nicht direkt veröffentlicht werden.


Hinweis

In OnStart dürfen keine Operationen, die länger als 30 Sekunden beanspruchen, ausgeführt werden, da der Service Control Manager (SCM) ansonsten den Start des Dienstes als gescheitert ansieht (die Fehlermeldung wird im Ereignisprotokoll System angezeigt). Eine gute Lösung ist es, die Operationen einem separaten Thread zuzuordnen, der in OnStart initialisiert und gestartet wird. Müssen die Operationen nicht kontinuierlich, sondern in gleichen Abständen wiederholt werden, bietet sich das Timer-Steuerelement in der Registerkarte Komponenten der Toolbox an.


Angestoßen wird OnStart durch den Service Control Manager. Beim Booten des Systems ist das der Fall, wenn im Eigenschaftsdialog des Dienstes unter Starttyp Automatisch eingestellt ist (siehe auch Abbildung 24.2). Mit Manuell wird der Dienst beim Booten zwar in den Speicher geladen, wartet aber auf ein äußeres Signal, um seine Aufgaben auszuführen. Das kann durch Klicken der Schaltfläche Starten im Dialog erfolgen oder durch Programmcode. Analog verhält sich auch die Methode OnStop. Sie erhält einen Anstoß von außen, wenn der Dienst beendet wird – entweder durch Klicken der Schaltfläche Beenden oder mittels Programmcode.

Wird ein Dienst gestartet, können ihm Startparameter übergeben werden, die im Dienste-Dialog im untersten Eingabefeld eingetragen werden (siehe Abbildung 24.2). Von der OnStart-Methode werden diese Parameter in einem String-Array entgegengenommen und können in der Methodenimplementierung ausgewertet werden, um das Laufzeitverhalten des Dienstes zu beeinflussen.


Protected Overridable Sub OnStart(ByVal args As String())

Die Methode OnStop hingegen ist parameterlos, ebenso wie alle anderen OnXxx-Methoden, die im folgenden Abschnitt beschrieben werden.


Galileo Computing

24.2.3 Die Methoden eines Dienstes  downtop

Ein Dienst, der gestartet ist, kann entweder angehalten oder beendet werden. Mit dem Beenden eines Dienstes kommt es zur Ausführung der Methode OnStop. Wird ein Dienst nur kurzzeitig angehalten, wird eine andere Methode aufgerufen: OnPause. Ein unterbrochener Dienst kann natürlich auch wieder gestartet werden, wobei die Methode OnContinue den Code enthält, der in diesem Fall ausgeführt werden soll.

Über die vier bisher erwähnten Methoden hinaus werden noch zwei weitere angeboten, die es ermöglichen, einen Dienst in Abhängigkeit von den Umgebungsbedingungen noch differenzierter zu steuern. Da wäre zuerst die Methode OnShutDown zu nennen. Während OnStop aufgerufen wird, wenn der Dienst beendet wird, aber das System weiterläuft, können in On-ShutDown Verhaltensweisen implementiert werden, die nur dann von Bedeutung sind, wenn das System heruntergefahren wird. Zu guter Letzt kann in einem Dienst auch noch berücksichtigt werden, dass ein Laptop wegen zu hoher Akkuspannung in den Stand-by-Modus wechselt. Das System informiert den Dienst in diesem Fall durch Aufruf von OnPowerEvent.


Tabelle 24.5     Geschützte Methoden der Klasse »ServiceBase«

Methode Beschreibung
OnContinue Wird ausgeführt, wenn der Dienst nach dem Anhalten wieder fortgesetzt wird.
OnPause Wird ausgeführt, wenn der Dienst angehalten wird.
OnPowerEvent Wird ausgeführt, wenn das System in den Stand-by-Modus geht.
OnStart Wird ausgeführt, wenn der Dienst gestartet wird.
OnStop Wird ausgeführt, wenn der Dienst beendet wird.
OnShutDowm Wird ausgeführt, wenn das System heruntergefahren wird.

Damit steht Ihnen eine Reihe von Methoden zur Verfügung, um das Verhalten eines Dienstes in allen denkbaren Situationen festzulegen. Welche Methoden überschrieben werden müssen, hängt von der Arbeitsweise des Dienstes ab. Mit Sicherheit werden Sie aber immer OnStart implementieren, damit der Dienst eine nützliche Funktionalität aufweist.


Galileo Computing

24.2.4 Die Eigenschaften eines Dienstes  downtop

Der SCM sendet Signale an den Dienst, die zur Folge haben, dass dieser darauf entsprechend reagiert. Beim Starten ist es ein Signal, das die Ausführung von OnStart im Dienst nach sich zieht, beim Beenden ein Signal, infolge dessen OnStop aufgerufen wird. Analog gilt das auch für die anderen Methoden. Auf einem anderen Blatt steht jedoch, ob der Dienst überhaupt in der Lage ist, das Signal zu empfangen und an die passende Methode weiterzuleiten. Diese Festlegung erfolgt in den Eigenschaften der von ServiceBase abgeleiteten Klasse. Um sich die Eigenschaften im Eigenschaftsfenster anzeigen zu lassen, müssen Sie den Designer der Dienstklasse aktivieren.

Insgesamt vier Eigenschaften legen die Empfangsbereitschaft eines Dienstes fest:

gp  CanHandlePowerEvent
gp  CanPauseAndContinue
gp  CanShutDown
gp  CanStop

Bis auf CanStop sind alle genannten Eigenschaften mit False vorinitialisiert. Die Konsequenzen lassen sich am besten erkennen, wenn Sie sich den Eigenschaftsdialog eines Dienstes ansehen. In der Abbildung 24.2 können die beiden Schaltflächen Anhalten und Fortsetzen nur dann aktiviert werden, wenn der Dienst gestartet und die Eigenschaft CanPauseAndContinue=True festgelegt ist. Theoretisch wäre es möglich, einen Dienst zu starten, ohne ihn jemals beenden zu können. Dazu müsste die Eigenschaft CanStop=False eingestellt werden. In der Praxis werden Sie aber vermutlich nur selten auf einen Fall stoßen, der es erlaubt, auf Operationen beim Beenden eines Dienstes zu verzichten.

Ein Dienst kann seine Zustandsänderungen in das Anwendungsereignisprotokoll des Systems schreiben. Voraussetzung dazu ist, dass die Eigenschaft AutoLog des Dienstes True gesetzt ist. Das ist gleichzeitig auch die Voreinstellung, bedarf also keiner Änderung.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 24.3     Das »Anwendungsereignisprotokoll«

Im Anwendungsereignisprotokoll (siehe Abbildung 24.3) wird in der Spalte Quelle der Bezeichner ausgegeben, der durch die Eigenschaft ServiceName der Dienstklasse vorgegeben ist. Ein Doppelklick auf einen Eintrag öffnet das Eigenschaftsfenster des Ereignisses, in dem über die allgemeinen Angaben zum Ereignis hinaus auch noch eine Beschreibung angezeigt wird. Standardmäßig ist die Beschreibung leer. Wollen Sie allerdings einen informativen Text ausgeben, steht Ihnen mit EventLog eine öffentliche, schreibgeschützte Instanzeigenschaft der Dienstklasse zur Verfügung, welche die Referenz auf das Ereignisprotokoll zurückliefert.

Zur Übergabe einer Information an das Ereignisprotokoll dient die Methode WriteEntry des EventLog-Objekts, der im einfachsten Fall nur eine Zeichenfolge übergeben wird, z.  B.:


Protected Overrides Sub OnStart(ByVal args() As String)
Me.EventLog.WriteEntry("DiskWatcher gestartet")
' weitere Anweisungen
End Sub

In Abbildung 24.4 ist zu sehen, wie sich die Information im Eigenschaftsfenster der Ereignisses darstellt. Wenn Sie die Wirkung jetzt selbst ausprobieren wollen, müssen Sie zuerst eine Installationsdatei des Dienstes kompilieren. Dazu kommen wir im folgenden Abschnitt.


Hinweis

Um Ereigniseinträge in andere Systemprotokolldateien zu schreiben, bieten sich Ihnen über die Klasse EventLog Möglichkeiten dazu (siehe auch Abschnitt 8.2.4). In der Toolbox wird Ihnen zur Erleichterung Ihrer Arbeit für diese Klasse auch ein Control angeboten.


Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 24.4     Eigenschaftsdialog eines Eintrags im Ereignisprotokoll


Tabelle 24.6     Eigenschaften der Klasse »ServiceBase«

Eigenschaften Beschreibung
AutoLog Gibt an, ob die Befehle zum Starten, Beenden, Anhalten und Fortsetzen im Ereignisprotokoll Anwendung aufgezeichnet werden sollen.
CanHandlePowerEvent Gibt an, ab der Dienst Benachrichtigungen, die durch einen zu niedrigen Ladezustand der Akkus ausgelöst werden, verarbeiten kann.
CanPauseAndContinue Gibt an, ob der Dienst angehalten und wieder fortgesetzt werden kann.
CanShutDown Gibt an, ob der Dienst beim Herunterfahren des Systems benachrichtigt werden soll.
CanStop Gibt an, ob der Dienst nach dem Starten beendet werden kann.
ServiceName Gibt die Bezeichnung des Dienstes an.


Galileo Computing

24.2.5 Die Installation eines Windows-Dienstes  downtop

Damit hätten wir schon die Beschreibung der Bereitstellung eines Windows-Dienstes abgeschlossen, und es bleibt nur noch, den Dienst zu installieren. Damit der SCM den Dienst auch finden kann, muss er in die Registrierungsdatenbank eingetragen werden. Obwohl diese Aufgabe im ersten Moment schwierig erscheint, unterstützt uns das Visual Studio 2005 in dieser Hinsicht recht gut, denn dazu sind nur ein paar wenige Klicks und Einträge erforderlich.

Zwei Komponenten nehmen Ihnen einen Großteil der Arbeit ab: ServiceProcessInstaller und ServiceInstaller. Beide werden als Steuerelemente bereitgestellt, müssen jedoch zuvor der Toolbox hinzugefügt werden. Ziehen Sie beide Komponenten jeweils einmal in den Designer. ServiceProcessInstaller übernimmt die Installation der Installationsdatei des Dienstes auf dem System, ServiceInstaller trägt die Dienstkomponente in die Registry unter


HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services

ein.

In beiden Objekten sind nur wenige Eigenschaften bis zur endgültigen Fertigstellung der installationsfähigen Windows-Dienstanwendung festzulegen. Im ServiceProcessInstaller-Objekt ist das die Eigenschaft Account, mit der die Benutzerrechte des Dienstes bestimmt werden. In den meisten Fällen erweist sich LocalSystem als am besten geeignet.


Anmerkung

Weitere Informationen zu den drei weiteren Alternativen, den Sicherheitskontext eines Dienstes festzulegen, entnehmen Sie bitte der Online-Dokumentation.


Im ServiceInstaller-Objekt sollten wir drei Eigenschaften berücksichtigen. Da wäre zuerst StartType zu nennen, mit der festgelegt wird, wie sich der Dienst nach dem Laden verhalten soll. Bekanntlich kann ein Dienst automatisch oder manuell gestartet werden. Die dritte sich anbietende Alternative wäre es, den Dienst zu deaktivieren, so dass er weder vom Benutzer noch durch ein Programm gestartet werden kann.

Einen weniger gravierenden Einfluss haben die beiden Eigenschaften DisplayName und ServiceName. Trotzdem sollten auch diese Eigenschaftseinstellungen sorgfältig vergeben werden. Der Text, den Sie unter DisplayName eintragen, wird im Dialog Dienste angezeigt, die Einstellung von ServiceName wird als Schlüssel in der Registry benutzt.

Damit sind alle vorbereitenden Arbeiten erledigt, und der Installer ist konfiguriert. Das Projekt muss anschließend nur noch kompiliert werden. Hier ist allerdings ein Hinweis notwendig, denn beim Kompilieren wird Ihnen eine Reihe von Kompilierfehlern angezeigt. Die Ursache liegt darin, dass in der Anwendung nicht automatisch auf die Assembly System.Configuration.Install.dll verwiesen wird. Sie müssen das manuell vornehmen. Das Ergebnis der Kompilierung ist eine EXE-Datei, die Installationsdatei des Windows-Dienstes.

Jetzt kommt es zur Installation auf dem Zielsystem. Dazu bieten sich zwei Alternativen an:

gp  eine Installationsroutine mit einem Weitergabeprojekt
gp  das Tool InstallUtil.exe

An dieser Stelle wollen wir auf die erstgenannte Möglichkeit verzichten. In Kapitel 28, in dem wir uns mit den Weitergabeprojekten noch ausgiebig beschäftigen, wird die Weitergabe eines Windows-Dienstes noch einmal Thema sein. Stattdessen wollen wir hier das Tool Install-Util.exe benutzen, das im Verzeichnis


\Windows\Microsoft.NET\Framework\v<Versionsnummer>

zu finden ist. Zum Installieren rufen Sie das Tool an der Konsole auf und übergeben als Parameter die Installationsdatei des Windows-Dienstes, also beispielsweise:


InstallUtil FileWatchService.exe

Ähnlich einfach kann ein Dienst auch deinstalliert werden. Dazu wird der Aufruf des Tools um den Optionsschalter /u ergänzt:


InstallUtil /u FileWatchService.exe

Wenn die Installation erfolgreich abgelaufen ist, werden Sie den selbst geschriebenen Windows-Dienst nun im Dienste-Dialog zu sehen bekommen.


Galileo Computing

24.2.6 Beispielprogramm »FileWatchService«  toptop

Nun wollen wir unsere Kenntnisse einsetzen und einen Windows-Dienst vollständig implementieren. Dieser Dienst, dessen Name FileWatchService lautet, soll in der Lage sein, alle Änderungen am Dateisystem in einer Datei zu protokollieren, beispielsweise das Löschen, Ändern und Hinzufügen einer Datei. Außerdem soll der Dienst auch angehalten und erneut gestartet werden können.

Weil der Dienst permanent auf eingehende Änderungsmitteilungen lauscht und die OnStart-Methode das Zeitlimit von 30 Sekunden nicht überschreiten darf, wird die gesamte Funktionalität in der separaten Klasse Worker implementiert und in einem eigenen Thread ausgeführt.


' ----------------------------------------------------------
' Beispiel: ...\Kapitel 24\FileWatchService
' ----------------------------------------------------------
Imports System.Threading
Public Class Service1
Private myThread As Thread
Private path As String = ""
Protected Overrides Sub OnStart(ByVal args() As String)
If args.Length <> 0 Then
Me.path = args(0)
End If
Dim work As Worker = New Worker(Me.path)
myThread = New Thread(New ThreadStart( _
AddressOf work.StartWorker))
myThread.Start()
Me.EventLog.WriteEntry( _
"FileWatchService wurde erfolgreich gestartet")
End Sub
Protected Overrides Sub OnStop()
myThread.Resume()
myThread.Abort()
myThread = Nothing
Me.EventLog.WriteEntry("FileWatchService wurde beendet")
End Sub
Protected Overrides Sub OnPause()
myThread.Suspend()
Me.EventLog.WriteEntry("FileWatchService wurde angehalten")
End Sub
Protected Overrides Sub OnContinue()
myThread.Resume()
Me.EventLog.WriteEntry("FileWatchService wurde fortgesetzt")
End Sub
End Class

In OnStart wird zuerst ein Objekt vom Typ Worker bereitgestellt. Dem Konstruktor der Klasse wird eine Zeichenfolge übergeben, die eine Verzeichnisangabe enthält. In diesem und allen untergeordneten Verzeichnissen sollen die Änderungen protokolliert werden. Im Eigenschaftsdialog des Dienstes kann der Anwender das Verzeichnis im Eingabefeld Startparameter selbst bestimmen, ansonsten werden sämtliche Änderungen im Laufwerk C:\ festgehalten. Nach der Initialisierung des Arbeitsthreads und dem Aufruf der Startmethode schreibt OnStart nur noch eine Mitteilung in das Anwendungsereignisprotokoll.

In den anderen Methoden wird nur noch das Verhalten des Threads gesteuert: In OnPause wird der Thread in den Wartezustand versetzt, in OnContinue wird er bereit geschaltet. Das Stoppen des Dienstes durch Aufruf der Methode OnStop zerstört den Thread.

Sehen wir uns nun an, wie die Klasse Worker konstruiert ist.


Imports System.Windows.Forms
Imports System.IO
Imports System.Collections.Specialized
Public Class Worker
Private protocolFile As String = Application.StartupPath & _
"\FileWatchLog.log"
Private path As String = "c:\"
' Konstruktor
Public Sub New(ByVal path As String)
If path <> "" Then
Me.path = path
End If
End Sub
' Startmethode des Threads
Public Sub StartWorker()
Dim fsw As FileSystemWatcher = _
New FileSystemWatcher(Me.path)
' Überwachen aller Unterverzeichnisse einschließen
fsw.IncludeSubdirectories = True
' Eventauslösung aktivieren
fsw.EnableRaisingEvents = True
' Ereignishandler
AddHandler fsw.Changed, AddressOf ChangeFile
AddHandler fsw.Deleted, AddressOf DeleteFile
AddHandler fsw.Created, AddressOf CreateFile
AddHandler fsw.Renamed, AddressOf RenameFile
Try
' in dieser Schleife wird auf etwaige Änderungen im
' Dateisystem gewartet
Do While (True)
fsw.WaitForChanged(WatcherChangeTypes.All)
Loop
Catch
End Try
' Eventauslösung deaktivieren
fsw.EnableRaisingEvents = False
End Sub
' Datei wurde umbenannt
Private Sub RenameFile(ByVal sender As Object, _
ByVal e As RenamedEventArgs)
Dim entry As String = "[" & _
DateTime.Now.ToShortTimeString() & _
"] Umbenannt: " & e.FullPath
WriteToLogFile(entry)
End Sub
' Datei wurde gelöscht
Private Sub DeleteFile(ByVal sender As Object, _
ByVal e As FileSystemEventArgs)
Dim entry As String = "[" & _
DateTime.Now.ToShortTimeString() & _
"] Gelöscht: " & e.FullPath
WriteToLogFile(entry)
End Sub
' Datei wurde neu erzeugt
Private Sub CreateFile(ByVal sender As Object, _
ByVal e As FileSystemEventArgs)
Dim entry As String = "[" & _
DateTime.Now.ToShortTimeString() & _
"] Erzeugt: " & e.FullPath
WriteToLogFile(entry)
End Sub
' Datei wurde geändert
Private Sub ChangeFile(ByVal sender As Object, _
ByVal e As FileSystemEventArgs)
' Änderungen in der Protokolldatei nicht aufzeichnen
If (e.FullPath.ToLower() = protocolFile.ToLower()) Then
Return
End If
Dim entry As String = "[" & _
DateTime.Now.ToShortTimeString() & _
"] Geändert: " & e.FullPath
WriteToLogFile(entry)
End Sub
Private Function ReadFromLogFile() As StringCollection
Dim strCol As StringCollection = New StringCollection()
Dim sr As StreamReader = _
New StreamReader(Me.protocolFile)
Dim line As String = ""
Dim count As Integer = 1
Do While (sr.ReadLine() IsNot Nothing)
' maximal 999 Einträge lesen
line = sr.ReadLine()
If (count = 999) Then
Exit Do
End If
strCol.Add(line)
count += 1
Loop
sr.Close()
Return strCol
End Function
' Schreibt eine Änderung in die Protokolldatei
Private Sub WriteToLogFile(ByVal entry As String)
Dim changedEntriesCol As StringCollection
If (File.Exists(Me.protocolFile)) Then
changedEntriesCol = ReadFromLogFile()
Else
changedEntriesCol = New StringCollection()
End If
' neuen Eintrag an erster Stelle einfügen
changedEntriesCol.Insert(0, entry)
' Schreibt Einträge in eine Log-Datei, dabei neue Datei
' erzeugen oder alte überschreiben
Dim fs As FileStream = New FileStream(Me.protocolFile, _
FileMode.Create)
Dim sw As StreamWriter = New StreamWriter(fs)
For Each str As String In changedEntriesCol
sw.WriteLine(str)
Next
sw.Close()
End Sub
End Class


Tipp

Wenn Sie eine Klasse ähnlich Worker entwickeln, welche die Funktionalität eines Windows-Dienstes implementiert, sollten Sie die Klasse vorher außerhalb eines Windows-Dienstprojekts ausgiebig testen. Sie ersparen sich damit viel Zeit, die investiert werden müsste, um den geänderten Dienst immer wieder neu zu installieren und zu deinstallieren.


Der gesamten Funktionalität des Dienstes liegt eine Klasse aus dem Namespace System.IO zugrunde: FileSystemWatcher. Auch diese Klasse wird Ihnen als Steuerelement in der Toolbox zur Verfügung gestellt. Objekte dieses Typs lösen Ereignisse aus, wenn eine Datei oder ein Verzeichnis verändert wird. Das Verzeichnis, ab dem überwacht werden soll, wird dem Konstruktor übergeben. Die Ereignisse, die ausgelöst werden, heißen Changed, Created, Deleted und Renamed.

Wird der Dienst gestartet und die OnStart-Methode aufgerufen, wird zuerst ein Objekt vom Typ der Klasse Worker erzeugt. Dabei wird das zu überwachende Verzeichnis übergeben und im Feld path gespeichert. Die Startmethode des Threads ist StartWorker, in der das FileSystemWatcher-Objekt erzeugt wird. Mit der Eigenschaft IncludeSubdirectories wird festgelegt, dass auch alle Unterverzeichnisse überwacht werden sollen, und mit EnableRaising-Events die Ereignisauslösung aktiviert. Nach dem Binden der Ereignishandler an die Events kommt der Kern des gesamten Dienstes: der Aufruf der Methode WaitForChanged des FileSystemWatcher-Objekts. Diese Methode wartet für einen unbegrenzten Zeitraum, bis eine Änderung eintritt, und gibt dann eine Rückgabe aus, die uns aber nicht weiter interessiert. Wichtig ist vielmehr, dass die Methode in einer Schleife lauert und das Beenden der Routine verhindert. Solange die Schleife nicht durch einen äußeren Einfluss, bei uns ist das der aufrufende Thread in der Dienstklasse, beendet wird, werden bei Änderungen am überwachten Verzeichnis die entsprechenden Ereignisse ausgelöst.

Aufgezeichnet werden die Veränderungen in einer Protokolldatei, die sich im gleichen Verzeichnis wie die ausführbare Datei des Dienstes befindet. In allen vier Ereignishandlern werden entsprechende Textinformationen über den Aufruf der benutzerdefinierten Methode WriteToLogFile in die Protokolldatei geschrieben. Allerdings müssen wir vorsichtig sein, was die Überwachung von Änderungen angeht. Befindet sich nämlich die Protokolldatei im überwachten Verzeichnis, wird sie natürlich selbst auch verändert und löst damit das Change-Ereignis erneut aus – der klassische Fall einer Endlosschleife. Daher wird im Ereignishandler ChangeFile zuerst geprüft, ob eine Änderung der Protokolldatei das Ereignis ausgelöst hat. In diesem Fall muss sich die Protokolldatei nicht selbst protokollieren.

Der in die Protokolldatei zu schreibende Eintrag wird der Methode WriteToLogFile übergeben. WriteToLogFile überprüft zuerst, ob die Protokolldatei bereits existiert. Ist das der Fall, wird sie mit der Methode ReadFromLogFile eingelesen. Jeder Eintrag in der Protokolldatei nimmt eine Zeile in Anspruch. Damit die Datei nicht unzulässig anwächst, ist die Maximalgröße im Programmcode auf 1000 Einträge beschränkt. Das wird dadurch erreicht, dass nur bis zu 999 Einträge eingelesen werden. Zur einfacheren Verwaltung wird jeder Dateieintrag einem StringCollection-Objekt übergeben. Der neue Eintrag wird danach mit dem Index 0 in die Auflistung geschoben. Damit ist garantiert, dass ein Anwender nach dem Öffnen der Protokolldatei die aktuellsten Einträge immer oben findet.

 <<   zurück
  
  Zum Katalog
Zum Katalog: Visual Basic 2005
Visual Basic 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual C# 2005






 Visual C# 2005


Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Das Programmierhandbuch SQL Server 2005






 Das Programmier-
 handbuch
 SQL Server 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de